package com.xc.tool.utils;

import com.esotericsoftware.reflectasm.MethodAccess;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>项目名称: Stars</p>
 * <p>描述: Object的工具类</p>
 * <P>创建人: 小程</p>
 * <P>创建人邮箱: 779943476@qq.com</p>
 * <p>创建时间: 2019/7/1</p>
 *
 * @version v1.0
 */
public class ObjectUtils {
    /**
     * 方法map
     */
    private static Map<Class, MethodAccess> methodMap = new HashMap<Class, MethodAccess>();
    /**
     * 方法index的map
     */
    private static Map<String, Integer> methodIndexMap = new HashMap<String, Integer>();
    /**
     * 字段map
     */
    private static Map<Class, List<String>> fieldMap = new HashMap<Class, List<String>>();

    /**
     * <p>功能描述:对象转换，复制</p>
     * <p>更新人: 小程</p>
     * <p>更新描述: 添加方法</p>
     * <p>更新人邮箱: 779943476@qq.com</p>
     * <p>更新时间: 2019/7/1</p>
     *
     * @param t   转换后的类
     * @param obj 待转换的类
     * @param <T> 转换后的类型
     * @return 转换后的类
     */
    public static <T> T convert(T t, Object obj) {
        MethodAccess tMethodAccess = methodMap.get(t.getClass());
        if (tMethodAccess == null) {
            tMethodAccess = cache(t);
        }
        MethodAccess objMethodAccess = methodMap.get(obj.getClass());
        if (objMethodAccess == null) {
            objMethodAccess = cache(obj);
        }
        List<String> fieldList = fieldMap.get(obj.getClass());
        for (String field : fieldList) {
            String getKey = obj.getClass().getName() + "." + "get" + field;
            String setKey = t.getClass().getName() + "." + "set" + field;
            Integer setIndex = methodIndexMap.get(setKey);
            if (setIndex != null) {
                int getIndex = methodIndexMap.get(getKey);
                tMethodAccess.invoke(t, setIndex.intValue(), objMethodAccess.invoke(obj, getIndex));
            }
        }
        return t;
    }

    /**
     * <p>功能描述: 将方法，属性缓存到map同时初始化MethodAccess</p>
     * <p>更新人: 小程</p>
     * <p>更新描述: 添加方法</p>
     * <p>更新人邮箱: 779943476@qq.com</p>
     * <p>更新时间: 2019/7/1</p>
     *
     * @param obj 待缓存对象
     * @return 转换后的类
     */
    private static MethodAccess cache(Object obj) {
        synchronized (obj.getClass()) {
            MethodAccess methodAccess = MethodAccess.get(obj.getClass());
            List<Field> list = getField(new ArrayList<Field>(), obj.getClass());
            List<String> fieldList = new ArrayList<String>(list.size());
            for (Field field : list) {
                if (Modifier.isPrivate(field.getModifiers()) && !Modifier.isStatic(field.getModifiers())) { // 是否是私有的，是否是静态的
                    // 非公共私有变量
                    String fieldName = StringUtils.capitalize(field.getName()); // 获取属性名称
                    int getIndex = methodAccess.getIndex("get" + fieldName); // 获取get方法的下标
                    int setIndex = methodAccess.getIndex("set" + fieldName); // 获取set方法的下标
                    methodIndexMap.put(obj.getClass().getName() + "." + "get" + fieldName, getIndex); // 将类名get方法名，方法下标注册到map中
                    methodIndexMap.put(obj.getClass().getName() + "." + "set" + fieldName, setIndex); // 将类名set方法名，方法下标注册到map中
                    fieldList.add(fieldName); // 将属性名称放入集合里
                }
            }
            fieldMap.put(obj.getClass(), fieldList); // 将类名，属性名称注册到map中
            methodMap.put(obj.getClass(), methodAccess);
            return methodAccess;
        }
    }

    /**
     * <p>功能描述: 递归获取属性</p>
     * <p>更新人: 小程</p>
     * <p>更新描述: 添加方法</p>
     * <p>更新人邮箱: 779943476@qq.com</p>
     * <p>更新时间: 2019/7/1</p>
     *
     * @param list   储存获取的属性的集合
     * @param aClass 待处理的对象
     * @return 对象的全部属性
     */
    private static List<Field> getField(List<Field> list, Class<?> aClass) {
        if (aClass.getSuperclass() == null) {
            for (Field field : aClass.getDeclaredFields()) {
                list.add(field);
            }
            return list;
        } else {
            for (Field field : aClass.getDeclaredFields()) {
                list.add(field);
            }
            return getField(list, aClass.getSuperclass());
        }
    }
}
